Streamline AGENTS.md files and regroup WhereCore sources#89
Merged
Conversation
Add a GitHub section (always use gh; PRs open ready-for-review; keep open PRs current by pushing each local commit), route the Working on plans push rule through it, and extend Working on PR feedback: reply to a comment when a commit resolves it, and file deferred feedback somewhere durable rather than dropping it. Plan step: Root AGENTS.md — GitHub rules. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the libraries/targets enumerations with pointers at Package.swift / Project.swift (the stale list was missing WhereIntents and SwiftDataInspector — proof this content rots) and cut the directory-layout narrative down to the module skeleton. Keep the add-a-target recipe, the CI-scheme rule, and the double-linking lesson verbatim. Add four conventions mined from the last 30 days of PR review: synthesized/rename-safe Codable, retained observer tokens with paired stop(), grouping large flat types into sub-structs/child types, and protocol-conforming test doubles. Plan step: Root AGENTS.md — trim enumerations. Co-authored-by: Cursor <cursoragent@cursor.com>
Shrink the Modules section to one pointer line per module (their own AGENTS.md files are the authority), drop the developer-overlay tour, and cut descriptive feature content from the layering bullets while keeping every enforced rule. Point the stylesheet paragraph at WhereUI/AGENTS.md instead of duplicating it. Add two review-mined bullets under SwiftUI views & previews: accessibility-modal full-screen surfaces (+ focus notifications), and derive-don't-repeat UI dimensions (preference-key measurement, ScaledMetric, semantic font styles). Plan step: Where/AGENTS.md trim. Co-authored-by: Cursor <cursoragent@cursor.com>
Cut dependency listings that mirror Package.swift and PeriscopeCore's directory-layout catalog (the folder names are self-describing); keep every invariant and all WhereStylesheet guidance. Add one review-mined WhereCore invariant: post-write reconciliation is defined once — writes/imports route through DayJournal.reconcileAfterDayChange(), and cross-collaborator hooks are a single closure (BackupCoordinator.onImport), not injected reconciler lists. Plan step: Light trims — WhereUI, WhereCore, PeriscopeCore, WhereIntents AGENTS.md. Co-authored-by: Cursor <cursoragent@cursor.com>
Pure file moves, no behavior change. Fold the stragglers into their existing groups (BackupCoordinator -> Backup/, the three reconcilers -> Reminders/, WidgetSnapshotPublisher -> Widgets/, LocationIngestor + LocationSample -> Location/, EvidenceReader -> Evidence/, DismissedIssue -> DataResolution/, StoreChangeBroadcaster -> Persistence/) and add four new groups: Days/ (CalendarDay and the day/calendar helpers), Reporting/ (YearReport, MissingDays, ReportReader), Journal/ (DayJournal, ManualEntryAudit), and Preferences/ (WherePreferences, KeyValueStore, InMemoryKeyValueStore). Only the composition root (WhereServices) and the WhereLog facade stay at the top level. Tests stay flat, matching PeriscopeCore. Verified with the full Stuff-iOS-Tests scheme (1183 tests, 0 failures). Plan step: Regroup WhereCore Sources into subdirectories. Co-authored-by: Cursor <cursoragent@cursor.com>
kyleve
added a commit
that referenced
this pull request
Jul 15, 2026
…store-backed tracked regions (#87) ## What & why Prepares RegionKit for "more locations" ahead of letting users pick their own regions. Two coupled shifts: 1. **Split** the ~2.5 MB monolithic `us-states.geojson` into one GeoJSON file per region, loaded **on demand** — so we only ever parse the regions we track, not the whole US at launch. 2. **Replace** the hardcoded five-case `Region` enum with a **data-driven catalog** (a bundled `regions.json` manifest + a `Region` value type), so any US state (50 + DC + PR) plus Canada/EU are available regions. The user's **tracked** regions live in the synced SwiftData store (WhereCore owns "which"; RegionKit stays the agnostic geometry/lookup engine). Onboarding's region picker and user-chosen per-region styling are **out of scope** here (designed for, not built). ## What changed - **RegionKit — data-driven catalog + on-demand geometry** - `Region` is now a `Hashable`/`Codable` value type over a stable id (`us-CA`, `canada`, …) with a well-known `.other` and conveniences (`.california`, etc.). `RegionCatalog` loads the bundled `regions.json` manifest (all/name/localizedName/geometry file, canonical order). Adding a region is now **pure data** — a manifest + geojson change generated by `Tools/generate-regions.rb`, no code. - `RegionAttributor(for:)` loads only the passed regions' files; `.all` covers the whole catalog, `.shared` the default four. `RegionAttributing` abstracts the engine so the app can supply a live, swappable attributor. `RegionGeometryCatalog.outlines(for:attributor:)` takes an explicit attributor. - **WhereCore — store-backed, synced tracked regions + live attributor** - Tracked regions persist as **one `SDTrackedRegion` row per region** (so concurrent cross-device edits merge instead of last-write-wins), read as a `Set` defaulting to the four until the user chooses. - `RegionAttribution` rebuilds the attributor from the tracked set on the store's `changes()` signal (local edit or remote CloudKit import). `WhereServices.make(...)` (async) derives it from the store; the app launch and the App Intents process (`WhereServices.forIntents()`, now async) both attribute against the same synced set. `make(...)` is the **sole public assembly entry**; the synchronous `init` is `@_spi(Testing)` for tests/previews. - **WhereUI / WhereIntents — catalog-driven** - `RegionStyle` is id-keyed with an isolated, disposable bespoke-override table (the seam user-chosen styling will replace). Ordering uses `Region.inCanonicalOrder(_:)` instead of scanning `Region.allCases`. - Siri's "pick a region" suggestions and the Spotlight index surface the **tracked** set; `RegionEntity` id resolution stays **full-catalog** (so "days in Texas" answers even when untracked). - **Backup** — tracked regions round-trip in the archive (additive field; v1 manifests decode to `[]`); `.replace` restores the set exactly, `.merge` unions it. ## Key decisions / tradeoffs - **Clean id scheme** (`us-CA` vs `california`): existing persisted region raw values change; data is re-exported/re-imported. The `NAME → id` map lives in `Tools/generate-regions.rb`. - **Localization tradeoff:** region names come from the manifest (with an optional `localizationKey` override), so region names lose static string-catalog extraction — consistent with how the App Intents `RegionEntity` already resolves names at runtime. - **Attribution priority** is the manifest's canonical order (regions are mutually exclusive at our resolution). ## Deferred (follow-ups) - The onboarding region picker and user-chosen per-region color/emoji/symbol/tint. - Seeding semantics (the default four apply only at zero rows; the picker will seed/replace) and materializing the implicit default before the first explicit edit. - **Untracking a region:** the store currently *deletes* the tracked-region row, which would re-attribute that region's past GPS days to `.other` on re-aggregation (manual days, stored as region sets, are safe). A `TODO` defers the soft-delete (mark inactive + load every ever-tracked region so history stays stable) to the picker work that defines the untrack UX. Not user-reachable today — nothing calls `setTrackedRegion(false)` outside tests. ## Testing - Full `Stuff-iOS-Tests` scheme green; `./swiftformat --lint` clean. - New/reworked coverage: catalog + value-type (`RegionTests`), subset-only-loads (`RegionAttributorTests`), rebuild-on-`changes()` + store round-trip (`RegionAttributionTests`, `TrackedRegionStoreTests`), end-to-end `make()` wiring (`WhereServicesTests`), tracked-set entity/suggestions (`RegionEntityTests`), and backup round-trip / legacy-manifest decode (`BackupServiceTests`, `BackupCoordinatorTests`). ## Review pass - **Self-review:** log (not swallow) tracked-region read failures; deterministic (canonical-ordered) attributor build; surface unknown stored region ids; backup round-trip (above). - **PR review feedback:** consolidated the RegionKit bundled-data docs into the main `README.md` (removed the separate `Resources/README.md`); documented why `Region`'s `Codable` is hand-written (bare id string vs the synthesized keyed object); made the sync `WhereServices.init` `@_spi(Testing)` so `make()` is the sole public entry; and TODO'd the untrack soft-delete (see Deferred). ## Kept current with `main` Merged `#85` (CalendarDay), `#86` (Periscope JournalKit), `#88` (backup `onImport` reconcile hook), and `#89` (streamlined AGENTS + WhereCore source regroup). Conflicts resolved: the backup `formatVersion` (kept v2 **and** the additive `trackedRegions` field); the `BackupCoordinatorTests` harness swap (kept both the tracked-region tests and `#88`'s `onImport` hook test); and `Where/AGENTS.md` (took `#89`'s streamlined Modules section, but kept the accurate manifest-backed RegionKit localization note over `#89`'s stale "static keys" wording). My changes rode the source regroup into `Backup/`, `Persistence/`, `Days/`, `DataResolution/`, etc.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Trims the largest
AGENTS.mdfiles down to invariants, hard-fought lessons, and instructions — dropping structure enumerations agents re-derive from the manifests in their first minute of searching — and regroups WhereCore's flatSources/into concern-based subdirectories. One commit per step.Guiding principle: an AGENTS.md keeps invariants, lessons, conventions, and recipes; it drops module lists, directory trees, and dependency catalogs that mirror
Package.swift/Project.swift. The root file's libraries list was already stale (missingWhereIntentsandSwiftDataInspector) — proof that content rots.Commits
ghfor all GitHub interaction; open PRs ready-for-review, never draft; keep open PRs current by pushing each local commit. "Working on PR feedback" now requires replying to a comment when a commit resolves it, and filing deferred feedback durably instead of dropping it.WhereUI/AGENTS.md. 222 → 189 lines.Package.swift-mirroring dependency listings; PeriscopeCore loses its directory catalog. All invariants and the fullWhereStylesheetguidance stay.git mv, no behavior change: stragglers fold intoBackup/,Reminders/,Widgets/,Location/,Evidence/,DataResolution/,Persistence/; newDays/,Reporting/,Journal/,Preferences/groups. OnlyWhereServices*andWhereLogremain top-level. Tests stay flat, matching PeriscopeCore.New lessons codified (mined from the last 30 days of PR review)
Codable; persisted wire formats must survive Swift renames; hand-written conformances document their load-bearing reason (Make stored day-records timezone-independent with CalendarDay #85, Add crash-durability journaling to Periscope (JournalKit) #86)starthas a pairedstop()(Add Periscope: a typed, hierarchical observability framework #69)@ScaledMetric, semantic font styles) instead of repeating magic numbers (Custom Where launch splash with pulsing icon, radar pings, and zoom reveal #38, Move developer tools into a global floating overlay #70, Add App Intents / Siri integration to Where #83)DayJournal.reconcileAfterDayChange(); cross-collaborator hooks are a single closure (Move manual-day logging to a Primary toolbar item; year picker to Settings #80, Reconcile badge / issue count after a backup import #88)Verification
./swiftformat --lintclean;./sync-agentsrun after each docs commitStuff-iOS-Testsscheme after the regroup: 1183 tests, 0 failuresMade with Cursor